<?php


namespace app\common\util;

use think\cache\driver\Redis;

class RedisTool
{
    //config
    protected $config;
    //redis
    protected $redis;
    //数据前缀
    protected $prefix;

    public function __construct()
    {
        $this->config = config('cache.stores.redis');
        $this->prefix = $this->config['prefix'];
        $this->redis = new \Redis();
        $this->redis->connect($this->config['host'], $this->config['port']);
        $this->redis->auth($this->config['password']);
    }

    /**
     * @param $name
     * @param $arguments
     * @return false|mixed
     */
    public function __call($name, $arguments)
    {
        $last = count($arguments) - 1;
        $dbname = $arguments[$last];
        $this->switchDB($dbname);
        unset($arguments[$last]);
        $arguments = empty($arguments) ? array() : $arguments;
        return call_user_func_array(array($this, $name), $arguments);
    }

    /**
     * 格式化key
     * @param $key
     * @return string
     */
    protected function formatKey($key)
    {
        return $this->prefix . $key;
//        return $key;
    }

    /**
     * 序列化value
     * @param $value
     * @return mixed
     */
    protected function formatValue($value)
    {
        return @serialize($value);
//        return $value;
    }

    /**
     * 反序列化value
     * @param $value
     * @return mixed
     */
    protected function unformatValue($value)
    {
        return @unserialize($value);
//        return $value;
    }


    //---------------------------------------------------string类型-------------------------------------------------

    /**
     * 将value 的值赋值给key,生存时间为永久 并根据名称自动切换库
     */
    private function set_forever($key, $value)
    {
        return $this->redis->set($this->formatKey($key), $this->formatValue($value));
    }

    /**
     * 获取value 并根据名称自动切换库
     */
    private function get_forever($key)
    {
        $value = $this->redis->get($this->formatKey($key));
        return $value !== FALSE ? $this->unformatValue($value) : NULL;
    }

    /**
     * 存入一个有实效性的键值队
     */
    private function set_time($key, $value, $expire = 600)
    {
        return $this->redis->setex($this->formatKey($key), $expire, $this->formatValue($value));
    }


    /**
     * 更新具有有效时间key的value，不重置有效时间
     */
    private function save_time($key, $value)
    {
        if ($this->get_exists($key)) {
            $ttl = $this->get_time_ttl($key);
            return $this->set_time($key, $value, $ttl);
        }
        return NULL;
    }

    /**
     * 统一get/set方法,对于set_Time使用get_Time
     */
    private function get_time($key)
    {
        $value = $this->redis->get($this->formatKey($key));
        return $value !== FALSE ? ($this->unformatValue($value) ?: $value) : NULL;
    }

    /**
     * 得到一个key的生存时间
     */
    private function get_time_ttl($key)
    {
        $value = $this->redis->ttl($this->formatKey($key));
        return $value !== FALSE ? $value : NULL;
    }

    /**
     * 批量插入k-v,请求的v需要是一个数组 如下格式
     * array('key0' => 'value0', 'key1' => 'value1')
     */
    private function set_list($value)
    {
        $data = array();
        foreach ($value as $k => $v) {
            $data[$this->formatKey($k)] = $this->formatValue($v);
        }
        return $this->redis->mset($data);
    }

    /**
     * 批量获取k-v,请求的k需要是一个数组
     */
    private function get_list($key)
    {
        $data = array();
        foreach ($key as $k => $v) {
            $data[] = $this->formatKey($v);
        }
        $rs = $this->redis->mget($data);
        foreach ($rs as $k => $v) {
            $rs[$k] = $this->unformatValue($v);
        }
        return $rs;
    }

    /**
     * 判断key是否存在。存在 true 不在 false
     */
    private function get_exists($key)
    {
        return $this->redis->exists($this->formatKey($key));
    }

    /**
     * 返回原来key中的值，并将value写入key
     */
    private function get_getSet($key, $value)
    {
        $value = $this->redis->getSet($this->formatKey($key), $this->formatValue($value));
        return $value !== FALSE ? $this->unformatValue($value) : NULL;
    }

    /**
     * string，名称为key的string的值在后面加上value
     */
    private function set_append($key, $value)
    {
        return $this->redis->append($this->formatKey($key), $this->formatValue($value));
    }

    /**
     * 返回原来key中的值，并将value写入key
     */
    private function get_strlen($key)
    {
        return $this->redis->strlen($this->formatKey($key));
    }

    /**
     * 自动增长
     * value为自增长的值默认1
     */
    private function get_incr($key, $value = 1)
    {
        return $this->redis->incr($this->formatKey($key), $value);
    }

    /**
     * 自动减少
     * value为自减少的值默认1
     */
    private function get_decr($key, $value = 1)
    {
        return $this->redis->decr($this->formatKey($key), $value);
    }
    //------------------------------------------------List类型-------------------------------------------------

    /**
     * 写入队列左边
     */
    private function set_lPush($key, $value)
    {
        return $this->redis->lPush($this->formatKey($key), $this->formatValue($value));
    }

    /**
     * 写入队列左边 如果value已经存在，则不添加
     */
    private function set_lPushx($key, $value)
    {
        if ($this->get_exists($this->formatKey($key))) {
            return $this->redis->lPushx($this->formatKey($key), $this->formatValue($value));
        } else {
            return $this->set_lPush($key, $value);
        }
    }

    /**
     * 写入队列右边
     */
    private function set_rPush($key, $value)
    {
        return $this->redis->rPush($this->formatKey($key), $this->formatValue($value));
    }

    /**
     * 写入队列右边 如果value已经存在，则不添加
     */
    private function set_rPushx($key, $value)
    {
        if ($this->get_exists($this->formatKey($key))) {
            return $this->redis->rPushx($this->formatKey($key), $this->formatValue($value));
        } else {
            return $this->set_rPush($key, $value);
        }

    }

    /**
     * 读取队列左边
     */
    private function get_lPop($key)
    {
        $value = $this->redis->lPop($this->formatKey($key));
        return $value != FALSE ? $this->unformatValue($value) : NULL;
    }

    /**
     * 读取队列右边
     */
    private function get_rPop($key)
    {
        $value = $this->redis->rPop($this->formatKey($key));
        return $value != FALSE ? $this->unformatValue($value) : NULL;
    }

    /**
     * 读取队列左边 如果没有读取到阻塞一定时间
     */
    private function get_blPop($key)
    {
        $value = $this->redis->blPop($this->formatKey($key), $this->config['blocking']);
        return $value != FALSE ? $this->unformatValue($value[1]) : NULL;
    }

    /**
     * 读取队列右边 如果没有读取到阻塞一定时间
     */
    private function get_brPop($key)
    {
        $value = $this->redis->brPop($this->formatKey($key), $this->config['blocking']);
        return $value != FALSE ? $this->unformatValue($value[1]) : NULL;
    }

    /**
     * 名称为key的list有多少个元素
     */
    private function get_lSize($key)
    {
        return $this->redis->LLEN($this->formatKey($key));
    }

    /**
     * 返回名称为key的list中指定位置的元素
     */
    private function set_lSet($key, $index, $value)
    {
        return $this->redis->lSet($this->formatKey($key), $index, $this->formatValue($value));
    }

    /**
     * 返回名称为key的list中指定位置的元素
     */
    private function get_lGet($key, $index)
    {
        $value = $this->redis->lGet($this->formatKey($key), $index);
        return $value != FALSE ? $this->unformatValue($value[1]) : NULL;
    }

    /**
     * 返回名称为key的list中start至end之间的元素（end为 -1 ，返回所有）
     */
    private function get_lRange($key, $start, $end)
    {
        $rs = $this->redis->lRange($this->formatKey($key), $start, $end);
        foreach ($rs as $k => $v) {
            $rs[$k] = $this->unformatValue($v);
        }
        return $rs;
    }

    /**
     * 截取名称为key的list，保留start至end之间的元素
     */
    private function get_lTrim($key, $start, $end)
    {
        $rs = $this->redis->lTrim($this->formatKey($key), $start, $end);
        foreach ($rs as $k => $v) {
            $rs[$k] = $this->unformatValue($v);
        }
        return $rs;
    }

    //----------------------------------------------------set类型---------------------------------------------------

    /**
     * 添加单个集合
     */
    protected function set_sAdd($key, $value)
    {
        return $this->redis->SADD($this->formatKey($key), $this->formatValue($value));
    }

    /**
     * 获取集合的数量
     */
    protected function count_sSet($key)
    {
        return $this->redis->SCARD($this->formatKey($key));
    }

    /**
     * 随机获取集合
     */
    protected function get_sRand($key, $number)
    {
        $list = $this->redis->SRANDMEMBER($this->formatKey($key), $number);
        $data = [];
        foreach ($list as $item) {
            $data[] = $this->unformatValue($item);
        }
        return $data;
    }

    /**
     * 移除集合中一个
     */
    protected function del_sRem($key, $menber)
    {
        return $this->redis->SREM($this->formatKey($key), $this->formatValue($menber));
    }

    /**
     * 移除集合中多个
     */
    protected function del_sRems($key, $menbers)
    {
        foreach ($menbers as $menber) {
            $this->redis->SREM($this->formatKey($key), $this->formatValue($menber));
        }
        return true;
    }

    //----------------------------------------------------zset类型---------------------------------------------------

    /**
     * 添加单个有序集合
     */
    private function set_zAdd($key, $value, $score)
    {
        return $this->redis->ZADD($this->formatKey($key), $score, $this->formatValue($value));
    }

    /**
     * 按数组添加 score 数组分数参数，没有按照k值添加
     */
    private function set_zAdds($key, $value, $score = '')
    {
        foreach ($value as $k => $item) {
            if (isset($item[$score])) {
                $this->set_zAdd($key, $item, $item[$score]);
            } else {
                $this->set_zAdd($key, $item, $k);
            }
        }
        return true;
    }

    /**
     * 获取有序集合的数量
     */
    private function count_zSet($key)
    {
        return $this->redis->ZCARD($this->formatKey($key));
    }

    /**
     * 截取名称为key的list，保留start至end之间的元素
     */
    private function count_zTrim($key, $min, $max)
    {
        return $this->redis->ZCOUNT($this->formatKey($key), $min, $max);
    }

    /**
     * 有序集合成员分数自增
     */
    private function zset_incr($key, $increment, $value)
    {
        return $this->redis->ZINCRBY($this->formatKey($key), $increment, $this->formatValue($value));
    }

    /**
     * 获取指定索引内的成员 分数从低到高
     */
    private function get_zSet_asc($key, $start, $stop)
    {
        $data = [];
        $arr = $this->redis->ZRANGE($this->formatKey($key), $start, $stop);
        if (count($arr) > 0) {
            foreach ($arr as $value) {
                $data[] = $this->unformatValue($value);
            }
        }
        return $data;
    }

    /**
     * 获取指定索引内的成员 分数从高到底
     * @param $key
     * @param $start
     * @param $stop -1 为所有
     * @return array
     */
    private function get_zSet_desc($key, $start, $stop)
    {
        $data = [];
        $arr = $this->redis->ZREVRANGE($this->formatKey($key), $start, $stop);
        if (count($arr) > 0) {
            foreach ($arr as $value) {
                $data[] = $this->unformatValue($value);
            }
        }
        return $data;
    }

    /**
     * 删除成员
     */
    private function del_zSet($key, $value)
    {
        return $this->redis->ZREM($this->formatKey($key), $this->formatValue($value));
    }

    /**
     * 删除指定分数区间成员
     */
    private function del_zSets($key, $min, $max)
    {
        return $this->redis->ZREMRANGEBYSCORE($this->formatKey($key), $min, $max);
    }

    /**
     * 删除指定索引区间成员
     */
    private function del_zSeti($key, $min, $max)
    {
        return $this->redis->ZREMRANGEBYRANK($this->formatKey($key), $min, $max);
    }

    /**
     * 获取指定成员排名。分数由高到低
     */
    private function get_zSet_rank($key, $value)
    {
        return $this->redis->ZREVRANK($this->formatKey($key), $this->formatValue($value));
    }

    //----------------------------------------------------Hash类型---------------------------------------------------

    /**
     * 设置hash值的字段
     */
    private function set_hash($key, $field, $value)
    {
        return $this->redis->HSET($this->formatKey($key), $field, $value);
    }

    /**
     * 批量设置hash值的字段 value array 键值对
     */
    private function set_hashs($key, $value)
    {
        return $this->redis->HMSET($this->formatKey($key), $value);
    }

    /**
     * 获取hash字段
     */
    private function get_hash($key, $field)
    {
        return $this->redis->HGET($this->formatKey($key), $field);
    }

    /**
     * 获取hash指定字段 array
     */
    private function get_hashs($key, $fields)
    {
        return $this->redis->HMGET($this->formatKey($key), $fields);
    }

    /**
     * 获取所有hash字段
     */
    private function get_hash_all($key)
    {
        return $this->redis->HGETALL($this->formatKey($key));
    }

    /**
     * 查看hash值中是否存在字段
     */
    private function get_hash_exists($key, $field)
    {
        return $this->redis->HEXISTS($this->formatKey($key), $field);
    }

    /**
     * 删除hash中的某个字段
     */
    private function del_hash($key, $field)
    {
        return $this->redis->HDEL($this->formatKey($key), $field);
    }

    //----------------------------------------------------通用方法---------------------------------------------------

    /**
     * 永久计数器,回调当前计数
     * @author Axios <axioscros@aliyun.com>
     */
    private function counter_forever($key)
    {
        if ($this->get_exists($key)) {
            $count = $this->get_forever($key);
            $count++;
            $this->set_forever($key, $count);
        } else {
            $count = 1;
            $this->set_forever($key, $count);
        }

        return $count;
    }

    /**
     * 创建具有有效时间的计数器,回调当前计数,单位毫秒ms
     * @author Axios <axioscros@aliyun.com>
     */
    private function counter_time_create($key, $expire = 1000)
    {
        $count = 1;
        $this->set_time($key, $count, $expire);
        $this->redis->pSetEx($this->formatKey($key), $expire, $this->formatValue($count));
        return $count;
    }

    /**
     * 更新具有有效时间的计数器,回调当前计数
     * @author Axios <axioscros@aliyun.com>
     */
    private function counter_time_update($key)
    {
        if ($this->get_exists($key)) {
            $count = $this->get_time($key);
            $count++;
            $expire = $this->redis->pttl($this->formatKey($key));
            $this->set_time($key, $count, $expire);
            return $count;
        }
        return false;
    }

    /**
     * 设定一个key的活动时间（s）
     */
    private function setTimeout($key, $time = 600)
    {
        return $this->redis->setTimeout($key, $time);
    }

    /**
     * 返回key的类型值
     */
    private function type($key)
    {
        return $this->redis->type($key);
    }

    /**
     * key存活到一个unix时间戳时间
     */
    private function expireAt($key, $time = 600)
    {
        return $this->redis->expireAt($key, $time);
    }

    /**
     * key存活到一个unix时间戳时间
     */
    private function expire($key, $time = 600)
    {
        return $this->redis->expire($this->formatKey($key), $time);
    }

    /**
     * 随机返回key空间的一个key
     */
    private function randomKey()
    {
        return $this->redis->randomKey();
    }

    /**
     * 返回所有key
     */
    private function keys2($key)
    {
        return $this->redis->keys($key);
    }

    /**
     * 返回满足给定pattern的所有key
     */
    private function keys($key, $pattern)
    {
        return $this->redis->keys($key, $pattern);
    }

    /**
     * 查看现在数据库有多少key
     */
    private function dbSize()
    {
        return $this->redis->dbSize();
    }

    /**
     * 转移一个key到另外一个数据库
     */
    private function move($key, $db)
    {
        return $this->redis->move($key, $db);
    }

    /**
     * 给key重命名
     */
    private function rename($key, $key2)
    {
        return $this->redis->rename($key, $key2);
    }

    /**
     * 给key重命名 如果重新命名的名字已经存在，不会替换成功
     */
    private function renameNx($key, $key2)
    {
        return $this->redis->renameNx($key, $key2);
    }

    /**
     * 删除键值 并根据名称自动切换库(对所有通用)
     */
    private function del($key)
    {
        return $this->redis->del($this->formatKey($key));
    }

    /**
     * @param $key
     */
    private function delDoc($key)
    {
        $list = $this->keys2($key);
        foreach ($list as $one) {
            $this->del($one);
        }
    }

    /**
     * 返回redis的版本信息等详情
     */
    private function info()
    {
        return $this->redis->info();
    }

    /**
     * 切换DB并且获得操作实例
     */
    private function get_redis()
    {
        return $this->redis;
    }

    /**
     * 查看连接状态
     */
    private function ping()
    {
        return $this->redis->ping();
    }

    /**
     * 内部切换Redis-DB 如果已经在某个DB上则不再切换
     */
    protected function switchDB($name)
    {
        $arr = config('cache.stores.redis.DB');
        if (is_int($name)) {
            $db = $name;
        } else {
            $db = isset($arr[$name]) ? $arr[$name] : 0;
        }
        $this->redis->select($db);
    }

    //-------------------------------------------------------谨慎使用------------------------------------------------

    /**
     * 清空当前数据库
     */
    private function flushDB()
    {
        return $this->redis->flushDB();
    }

    /**
     * 清空所有数据库
     */
    private function flushAll()
    {
        return $this->redis->flushAll();
    }

    /**
     * 选择从服务器
     */
    private function slaveof($host, $port)
    {
        return $this->redis->slaveof($host, $port);
    }

    /**
     * 将数据同步保存到磁盘
     */
    private function save()
    {
        return $this->redis->save();
    }

    /**
     * 将数据异步保存到磁盘
     */
    private function bgsave()
    {
        return $this->redis->bgsave();
    }

    /**
     * 返回上次成功将数据保存到磁盘的Unix时戳
     */
    private function lastSave()
    {
        return $this->redis->lastSave();
    }

    /**
     * 使用aof来进行数据库持久化
     */
    private function bgrewriteaof()
    {
        return $this->redis->bgrewriteaof();
    }
}